signature TRANSLATE =
sig
    type level
    type access			(* not the same as FRAME.access *)
    type  frag
    datatype exp = Ex of Tree.exp (* not the same as Absyn.exp *)
		 | Nx of Tree.stm
		 | Cx of Temp.label * Temp.label -> Tree.stm

    val outermost : level
    val newLevel : {parent : level,
		    name : Temp.label,
		    formals : bool list} -> level
    val formals : level -> access list
    val allocLocal : level -> bool -> access

    val unEx : exp -> Tree.exp
    val unNx : exp -> Tree.stm
    val unCx : exp -> (Temp.label * Temp.label) -> Tree.stm

    val procEntryExit : {level : level, body : exp} -> unit

    structure Frame : FRAME
    val getResult : unit -> Frame.frag list
 
            val simpleVar : access * level -> exp
	    val nilExp : unit -> exp
	    val intExp : int -> exp
	    val stringExp : string -> exp
	    val callExp : level * Temp.label * exp list -> exp
	    val seqExp : exp list -> exp
	    val assignExp : exp * exp -> exp
	
	    val plusExp : exp * exp -> exp
	    val minusExp : exp * exp -> exp
	    val timesExp : exp * exp -> exp
	    val divExp : exp * exp -> exp
	    (*val noopExp : unit -> exp*)
	
	    val ifExp : exp * exp * exp -> exp
	    val ifStm     : exp * exp -> exp
	
	    val eqExp : exp * exp -> exp
	    val neqExp : exp * exp -> exp
	    val ltExp : exp * exp -> exp
	    val leExp : exp * exp -> exp
	    val gtExp : exp * exp -> exp
	    val geExp : exp * exp -> exp
	
	    val whileExp : exp * exp * Temp.label -> exp
	    val breakExp : Temp.label -> exp
	
	    val arrayExp : exp * exp -> exp
	    val subscriptExp : exp * exp -> exp

	       

end
 

structure Translate : TRANSLATE =
struct

  structure A = Absyn
  structure E = ErrorMsg
  structure F = MipsFrame
  structure M = Temp
  structure R = Tree 

  datatype level = Outermost of {frame : F.frame}
		 | FunLevel of {frame: F.frame, parent: level}

  type access = level * F.access

   type frag = F.frag

  fun newLevel {parent, name, formals}
      = FunLevel( {parent = parent, 
		frame = F.newFrame({name = name, formals = formals})} )


  val outermost = Outermost ({frame=F.newFrame({name=M.newlabel(),formals=[]})   }) 

(*Outermost({  F.newFrame({name=M.newlabel(),formals=[]})    }*)
 
  fun formals (Outermost {frame}) = nil
    | formals (level as FunLevel{frame,parent}) = 
       map (fn x => (level, x)) (F.formals(frame))

  fun allocLocal (level as Outermost{frame}) escape = 
      E.impossible " Impossible to allocate local Outermost level "
    | allocLocal (level as FunLevel{frame,parent}) escape = 
      		 let   val result = (level, F.allocLocal frame escape)
		 in 
		    result
		 end
 datatype exp = Ex of R.exp (* not the same as Absyn.exp *)
	       | Nx of R.stm
	       | Cx of M.label * M.label -> R.stm

fun seq (stm::nil) = stm
    | seq (stm::stms) = R.SEQ(stm, seq(stms))
    | seq (nil) = E.impossible("empty sequence")
					    
  
  fun unEx (Ex e) = e
  | unEx (Cx genstm) =
       let val r = M.newtemp()
       	   val t = M.newlabel() and f = M.newlabel()
       in R.ESEQ(seq[R.MOVE(R.TEMP r, R.CONST 1),
	                      genstm(t, f),
	                      R.LABEL f,
	                      R.MOVE(R.TEMP r, R.CONST 0),
	                      R.LABEL t],
	                  R.TEMP r)
	        end
   | unEx (Nx s) = R.ESEQ(s, R.CONST 0)
	                     
fun unNx (Ex e) = R.EXP(e)
     |  unNx (Nx s) = s    
     | unNx (Cx genstm) = 
       let val t = M.newlabel ()
       in R.SEQ (genstm(t,t), R.LABEL t) 
       end

fun unCx (Cx genstm : exp) : (M.label * M.label -> R.stm) =
	        (fn (t, f) => genstm(t, f))
     | unCx (Ex e) = (fn (t, f) => R.CJUMP(R.EQ, e, R.CONST(0), f, t))  



 val fraglist : F.frag list ref = ref []      

  fun insertFragment f = 
        fraglist := f :: (! fraglist) 

    fun getResult () = ! fraglist

 fun getFrame level=
     case level of
      	   Outermost ({frame=bframe})=> bframe
	  | FunLevel ({frame=bframe, parent=parent}) => bframe

  fun procEntryExit ({level, body}) = 
  let 
      val bframe = getFrame level     
      val rbody = R.MOVE(R.TEMP(F.RV), unEx body)
      val tbody= F.procEntryExit1(bframe, rbody)
      val i=insertFragment(F.PROC ({body=tbody, frame=bframe}))
  in 
     ()
  end

  structure Frame = F
  type frag = F.frag

  fun getResult () = ! fraglist

(************Translate expressions -> tree expressions******************************)
   
 fun simpleVar ((deflevel, access) : access, level : level) : exp =
	        let fun findStaticLink (deflevel, usagelevel) =
		    case usagelevel of 
		      FunLevel({parent=parent,frame=frame})=>
				if deflevel= usagelevel then R.TEMP(F.FP)
	                	else R.MEM(findStaticLink(deflevel, parent))
	              | _=> R.TEMP(F.FP)
	        in
	            Ex(F.exp(access)(findStaticLink(deflevel, level)))
	        end

    fun nilExp () = Ex(R.CONST(0))
    fun intExp ( n) = Ex(R.CONST(n))
    fun stringExp (s) =
	        let val slabel = M.newlabel()
	        in 
		   insertFragment(F.STRING(slabel, s));
	           Ex(R.NAME(slabel))
	        end
   fun callExp (_ : level, label, exps : exp list) =
	        Ex(R.CALL(R.NAME(label), map unEx exps))
   fun seqExp (exp::nil) : exp =
	        exp
             | seqExp (Nx(exp)::exps) =
               Ex(R.ESEQ(exp, unEx(seqExp(exps))))
             | seqExp (exp::exps) =
               Ex(R.ESEQ(R.EXP(unEx(exp)), unEx(seqExp(exps))))

   fun assignExp (expDest, expSrc) =
	        Nx(R.MOVE(unEx(expDest), unEx(expSrc)))

   fun binopExp binop (left, right) =
	        Ex(R.BINOP(binop, unEx(left), unEx(right)))
	
   fun plusExp pair = binopExp R.PLUS pair
   fun minusExp pair = binopExp R.MINUS pair
   fun timesExp pair = binopExp R.MUL pair
   fun divExp pair = binopExp R.DIV pair

   fun ifExp (test, then', else') =
        let val thenLabel = M.newlabel()
            val elseLabel = M.newlabel()
	    val joinLabel = M.newlabel()
	    val result = R.TEMP(M.newtemp())
	    in
	            Ex(R.ESEQ(seq[unCx(test)(thenLabel, elseLabel),
	                      seq[R.LABEL(thenLabel),
	                              R.MOVE(result, unEx(then')),
	                              R.JUMP(R.NAME joinLabel, [joinLabel])],
	                          seq[R.LABEL(elseLabel),
	                              R.MOVE(result, unEx(else')),
	                              R.JUMP(R.NAME joinLabel, [joinLabel])],
	                          R.LABEL(joinLabel)],
	                      result))
	        end

  fun ifStm (test, then') = 
      let val te = unCx test
    in case then' of 
	Ex _ => let val t = M.newlabel ()
		    val f = M.newlabel ()
		in Nx (seq [te(t,f), R.LABEL t, unNx then', R.LABEL f])
		end
      | Nx s => let val t = M.newlabel ()
		    val f = M.newlabel ()
		in Nx (seq [te(t,f), R.LABEL t, s, R.LABEL f])
		end
      | Cx c => let val t' = M.newlabel ()
		in Cx (fn (t,f) => 
		       seq [te(t',f), R.LABEL t', c(t,f)])
		end
    end


  fun  opExp cop (left, right) = 
       let fun rjump(t,f)=R.CJUMP(cop, unEx(left), unEx(right), t, f)
       in 
           Cx( rjump)
       end

 	
  fun eqExp pair = opExp R.EQ pair
  fun neqExp pair =opExp R.NE pair
  fun ltExp pair = opExp R.LT pair
  fun leExp pair = opExp R.LE pair
  fun gtExp pair = opExp R.GT pair
  fun geExp pair = opExp R.GE pair

  fun whileExp (test, body, donelabel) =
 	        let val testlabel = M.newlabel()
	            val startlabel = M.newlabel()
	        in
	            Nx(seq[R.LABEL(testlabel),
	                   unCx(test)(startlabel, donelabel),
	                   R.LABEL(startlabel),
	                   unNx(body),
	                   R.JUMP(R.NAME testlabel, [testlabel]),
	                   R.LABEL(donelabel)])
	        end

   fun breakExp label =
	        Nx(R.JUMP(R.NAME label, [label]))

   fun arrayExp(size, init) =
	        Ex(R.CALL(R.NAME(M.namedlabel("_initArray")),
	                  [unEx(size), unEx(init)]))
	
      fun subscriptExp(array, offset) =
	        let val address = Temp.newtemp()
	        in
	            Ex(R.ESEQ(R.MOVE(R.TEMP(address),
	                             R.BINOP(R.PLUS,
	                                     unEx(array),
	                                     R.BINOP(R.MUL,
	                                             unEx(offset),
	                                             R.CONST(Frame.wordSize)))),
	                      R.MEM(R.TEMP(address))))
	        end

end
